﻿using System.Collections.Generic;
using System.Globalization;
using Kyoh.Lib.Text;
using System.Text.RegularExpressions;

namespace Kyoh.App.Renamer
{
    public sealed class RenameOption : IPattern
    {
        private readonly Dictionary<string, int> _groupedNumbers = new Dictionary<string, int>();
        private readonly List<int> _numbers = new List<int>();
        private CreateIndexHandler _numberer;

        private int _number;

        private bool _grouped;
        private int _start;
        private int _step;
        private NumberStyle _style;
        private PatternMethod _method;
        private bool _ignoreCase;
        private bool _except;

        public bool Grouped { get { return _grouped; } }
        public int Start { get { return _start; } }
        public int Step { get { return _step; } }
        public NumberStyle Style { get { return _style; } }
        public PatternMethod Method { get { return _method; } }
        public bool IgnoreCase { get { return _ignoreCase; } }
        public bool Except { get { return _except; } }
        public string Pattern
        {
            get { return _replacer.Pattern; }
        }

        public string Replacement
        {
            get { return _replacer.Replacement; }
        }

        private IPattern _replacer;

        public RenameOption(bool grouped, int start, int step, NumberStyle style,
            PatternMethod method, string search, string replace, bool ignoreCase, bool except)
        {
            ApplyOption(grouped, start, step, style, method, search, replace, ignoreCase, except);
        }

        public bool ApplyOption(bool grouped, int start, int step, NumberStyle style,
            PatternMethod method, string search, string replace, bool ignoreCase, bool except)
        {
            bool changed = false;

            if (_replacer == null
                | changeProperty(ref _method, method)
                | changeProperty(ref _ignoreCase, ignoreCase)
                || Pattern != search
                || Replacement != replace)
            {
                switch (method)
                {
                    case PatternMethod.Characteristic:
                        _replacer = new Characteristic(search, replace, ignoreCase);
                        break;
                    case PatternMethod.Regex:
                        _replacer = new RegexPattern(search, replace, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
                        break;
                    case PatternMethod.Wildcard:
                        _replacer = new Wildcard(search, replace, ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
                        break;
                }
                changed = true;
            }

            changed |= changeProperty(ref _grouped, grouped);
            changed |= changeProperty(ref _start, start);
            changed |= changeProperty(ref _step, step);
            changed |= changeProperty(ref _style, style);
            changed |= changeProperty(ref _except, except);

            if (_grouped)
                _numberer = CreateGroupedNumber;
            else
                _numberer = CreateSerialNumber;
            _number = _start;

            return changed;
        }

        private static bool changeProperty<T>(ref T currentProperty, T newValue)
        {
            if (currentProperty.Equals(newValue))
                return false;
            currentProperty = newValue;
            return true;
        }

        public bool IsMatch(string input)
        {
            return _replacer.IsMatch(input);
        }

        public bool IsMatch(string input, int startat)
        {
            return _replacer.IsMatch(input);
        }

        public string Replace(string input, string replacement)
        {
            return _replacer.Replace(input, replacement);
        }

        public string Replace(string input, string replacement, int startat)
        {
            return _replacer.Replace(input, replacement, startat);
        }

        public string Replace(string input)
        {
            return _replacer.Replace(input);
        }

        public string Replace(string input, int startat)
        {
            return _replacer.Replace(input, startat);
        }

        private int CreateGroupedNumber(string @group)
        {
            int ret;
            if (_groupedNumbers.ContainsKey(@group))
            {
                ret = _groupedNumbers[@group];
                _groupedNumbers[@group] += _step;
            }
            else
            {
                ret = _start;
                _groupedNumbers.Add(@group, _number + _step);
            }
            return ret;
        }

        public string GetNumber(int index, int digit)
        {
            switch (_style)
            {
                case NumberStyle.Decimal:
                    return _numbers[index].ToString("D" + digit);
                case NumberStyle.Hexadecimal:
                    return _numbers[index].ToString("X" + digit);
                default:
                    return index.ToString(CultureInfo.InvariantCulture);
            }
        }

        private int CreateSerialNumber(string key)
        {
            var index = _number;
            _number += _step;
            return index;
        }

        private delegate int CreateIndexHandler(string key);

        public int CreateNumber(string key)
        {
            var n = _numberer(key);
            _numbers.Add(n);
            return n;
        }

        public void Reset()
        {
            _groupedNumbers.Clear();
            _numbers.Clear();
            _number = _start;
        }

        public override string ToString()
        {
            return (string.IsNullOrWhiteSpace(Pattern)?"[empty pattern]":Pattern)
                + " => "
                + (string.IsNullOrWhiteSpace(Replacement) ? "[empty replacement]" : Replacement);
        }
    }
}